﻿using System;
using System.Threading;
using System.Net.Sockets;
using System.IO;
using System.Collections;
using System.Text;
using Utils;

namespace MediaRemoteServer
{
    class ClientHandler
    {
        private const byte COMMAND_KEEP_ALIVE = 0;
        private const byte COMMAND_EXIT = 1;
        private const byte COMMAND_GET_SYSTEM_VOLUME = 2;
        private const byte COMMAND_SET_SYSTEM_VOLUME = 3;
        private const byte COMMAND_PLAY = 4;
        private const byte COMMAND_STOP = 5;
        private const byte COMMAND_PAUSE = 6;
        private const byte COMMAND_NEXT = 7;
        private const byte COMMAND_PREVIOUS = 8;
        private const byte COMMAND_GET_VOLUME = 9;
        private const byte COMMAND_SET_VOLUME = 10;
        private const byte COMMAND_GET_FULLSCREEN = 11;
        private const byte COMMAND_SET_FULLSCREEN = 12;
        private const byte COMMAND_GET_DURATION = 13;
        private const byte COMMAND_GET_POSITION = 14;
        private const byte COMMAND_SET_POSITION = 15;
        private const byte COMMAND_EXIT_PLAYER = 16;
        private const byte COMMAND_LIST_DIRECTORY = 17;
        private const byte COMMAND_SET_PLAYLIST = 18;
        private const byte COMMAND_GET_RATE = 19;
        private const byte COMMAND_SET_RATE = 20;
        private const byte COMMAND_IS_RUNNING = 21;
        private const byte COMMAND_IS_PAUSED = 22;
        private const byte COMMAND_SKIP_FORWARD_SHORT = 23;
        private const byte COMMAND_SKIP_FORWARD_LONG = 24;
        private const byte COMMAND_SKIP_BACKWARD_SHORT = 25;
        private const byte COMMAND_SKIP_BACKWARD_LONG = 26;

        private const long SKIP_SHORT = 1000 * 5;
        private const long SKIP_LONG = 1000 * 60;

        private Thread thread;
        private TcpClient client;
        private bool run;
        private MediaControl player;

        public ClientHandler(TcpClient client, MediaControl player)
        {
            this.client = client;
            this.player = player;
            thread = null;
            run = false;
        }

        private void process()
        {
            NetworkStream stream = client.GetStream();
            int byteRead = 0;
            byte vol;
            byte[] buf;
            bool fullscreen;
            long position;
            int length;
            try
            {
                while (run && (byteRead = stream.ReadByte()) != -1)
                {
                    Console.WriteLine("ByteRead: " + byteRead);
                    switch (byteRead)
                    {
                        case COMMAND_KEEP_ALIVE:
                            break;
                        case COMMAND_EXIT:
                            run = false;
                            break;
                        case COMMAND_GET_SYSTEM_VOLUME:
                            vol = (byte)VolumeControl.getVolumePercent();
                            stream.WriteByte(vol);
                            break;
                        case COMMAND_SET_SYSTEM_VOLUME:
                            vol = (byte)stream.ReadByte();
                            VolumeControl.setVolumePercent(vol);
                            break;
                        case COMMAND_PLAY:
                            player.play();
                            break;
                        case COMMAND_STOP:
                            player.stop();
                            break;
                        case COMMAND_PAUSE:
                            player.pause();
                            break;
                        case COMMAND_NEXT:
                            player.next();
                            break;
                        case COMMAND_PREVIOUS:
                            player.previous();
                            break;
                        case COMMAND_GET_VOLUME:
                            vol = (byte)player.getVolume();
                            stream.WriteByte(vol);
                            break;
                        case COMMAND_SET_VOLUME:
                            vol = (byte)stream.ReadByte();
                            player.setVolume(vol);
                            break;
                        case COMMAND_GET_FULLSCREEN:
                            fullscreen = player.isFullScreen();
                            stream.WriteByte(fullscreen ? (byte)1 : (byte)0);
                            break;
                        case COMMAND_SET_FULLSCREEN:
                            fullscreen = (stream.ReadByte() == 1);
                            player.setFullScreen(fullscreen);
                            break;
                        case COMMAND_GET_DURATION:
                            long duration = player.getDuration();
                            buf = Utils.BitConverter.GetBytes(duration);
                            stream.Write(buf, 0, buf.Length);
                            break;
                        case COMMAND_GET_POSITION:
                            position = player.getPosition();
                            buf = Utils.BitConverter.GetBytes(position);
                            stream.Write(buf, 0, buf.Length);
                            break;
                        case COMMAND_SET_POSITION:
                            buf = new byte[8];
                            stream.Read(buf, 0, buf.Length);
                            position = Utils.BitConverter.ToInt64(buf, 0);
                            Console.WriteLine("Setting position: " + position);
                            player.setPosition(position);
                            break;
                        case COMMAND_EXIT_PLAYER:
                            player.Dispose();
                            run = false;
                            break;
                        case COMMAND_LIST_DIRECTORY:
                            buf = new byte[4];
                            stream.Read(buf, 0, buf.Length);
                            length = Utils.BitConverter.ToInt32(buf, 0);
                            string dirName;
                            if (length > 0)
                            {
                                buf = new byte[length];
                                stream.Read(buf, 0, buf.Length);
                                dirName = Encoding.UTF8.GetString(buf);
                            }
                            else
                            {
                                dirName = "";
                            }
                            buf = listFiles(dirName);
                            stream.Write(buf, 0, buf.Length);
                            break;
                        case COMMAND_SET_PLAYLIST:
                            Playlist p = new Playlist();
                            byte numItems = (byte)stream.ReadByte();
                            Console.WriteLine("Num items: {0}", numItems);
                            for (int ii = 0; ii < numItems; ii++)
                            {
                                buf = new byte[4];
                                stream.Read(buf, 0, buf.Length);
                                length = Utils.BitConverter.ToInt32(buf, 0);
                                buf = new byte[length];
                                stream.Read(buf, 0, buf.Length);
                                string mrl = Encoding.UTF8.GetString(buf);
                                Console.WriteLine("MRL: {0}", mrl);
                                p.addItem(mrl);
                            }
                            player.playlist = p;
                            break;
                        case COMMAND_GET_RATE:
                            buf = Utils.BitConverter.GetBytes(player.getRate());
                            stream.Write(buf, 0, buf.Length);
                            break;
                        case COMMAND_SET_RATE:
                            buf = new byte[4];
                            stream.Read(buf, 0, buf.Length);
                            int rate = Utils.BitConverter.ToInt32(buf, 0);
                            player.setRate(rate);
                            break;
                        case COMMAND_IS_PAUSED:
                            bool paused = player.isPaused();
                            stream.WriteByte(paused ? (byte)1 : (byte)0);
                            break;
                        case COMMAND_IS_RUNNING:
                            bool running = player.isRunning();
                            stream.WriteByte(running ? (byte)1 : (byte)0);
                            break;
                        case COMMAND_SKIP_FORWARD_LONG:
                            player.setPosition(player.getPosition() + SKIP_LONG);
                            break;
                        case COMMAND_SKIP_FORWARD_SHORT:
                            player.setPosition(player.getPosition() + SKIP_SHORT);
                            break;
                        case COMMAND_SKIP_BACKWARD_LONG:
                            player.setPosition(player.getPosition() - SKIP_LONG);
                            break;
                        case COMMAND_SKIP_BACKWARD_SHORT:
                            player.setPosition(player.getPosition() - SKIP_SHORT);
                            break;
                    }
                }
            }
            catch (IOException ex) { Console.WriteLine(ex.ToString()); }
        }

        public bool isAlive()
        {
            return (thread != null && thread.IsAlive);
        }

        public void start()
        {
            thread = new Thread(new ThreadStart(process));
            run = true;
            thread.Start();
        }

        public void stop()
        {
            if (thread != null)
            {
                run = false;
                if (isAlive())
                {
                    thread.Join();
                }
            }
        }

        private static byte[] listFiles(string dir)
        {
            if (dir == null)
            {
                dir = "";
            }
            Console.WriteLine("Dir: {0}", dir);

            string[] files;
            string[] directories;
            byte[] data = null;

            if (dir == "" || dir == "..")
            {
                directories = Directory.GetLogicalDrives();
                files = new string[0];
            }
            else
            {
                directories = Directory.GetDirectories(dir);
                files = Directory.GetFiles(dir);
            }

            ArrayList dataArray = new ArrayList();
            dataArray.AddRange(Utils.BitConverter.GetBytes(directories.Length + files.Length));
            foreach (string directory in directories)
            {
                byte[] encString = Encoding.UTF8.GetBytes(directory);
                dataArray.Add((byte)1);//directory = true
                dataArray.AddRange(Utils.BitConverter.GetBytes(encString.Length));
                dataArray.AddRange(encString);
            }
            foreach (string file in files)
            {
                byte[] encString = Encoding.UTF8.GetBytes(file);
                dataArray.Add((byte)0);
                dataArray.AddRange(Utils.BitConverter.GetBytes(encString.Length));
                dataArray.AddRange(encString);
            }

            data = (byte[])dataArray.ToArray(typeof(byte));

            return data;
        }

    }
}
